
#include "debug.h"
#include "video.h"
#include "bitmap.h"
#include "format.h"
#include "semlock.h"
#include "fileio.h"
#include "netsend.h"

#include <stdio.h>
#include <stdlib.h>

#include <fcntl.h>
#include <unistd.h>

#include <sys/stat.h>

#include <linux/videodev2.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <time.h>
#include <signal.h>
#include <pthread.h>

#define N_BUF				1
#define THREAD_DEBUG		1
#define IGNORE_S_FMT		1

#define WIDTH				400
#define HEIGHT				240

#define SERVER_IP			(char *)"104.224.129.80"
#define SERVER_PORT			56032

static int loop = 0;
static int n_thread = 0;
static pthread_mutex_t mutex;

static void signal_handle(int signo)
{
	loop++;
}

static int ready_to_send(struct camera_class *cam, rgb32_class *rgb32, uint32_t len)
{
	int ret = 0;

	rgb24_class *rgb24_buf = NULL;
	bitmap_class *bmp = NULL;

	rgb24_buf = (rgb24_class *)calloc(1, cam->width * 3 * cam->height);
	
	if(NULL != rgb24_buf)
	{
		ret = rgb32_to_rgb24(rgb32, len, rgb24_buf, cam->width * 3 * cam->height, cam->width, cam->height);
		if(0 == ret)
		{
			bmp = init_bmp(cam->width, cam->height);
			if(NULL != bmp)
			{
				ret = bmp->mem_merge(bmp, rgb24_buf);
				if(0 == ret)
				{
					ret = send_to_server(SERVER_IP, SERVER_PORT, bmp);
					
					if(0 != ret)
					{
						WAR_DUMP("send to server was failed");
					}
				}
				else
				{
					WAR_DUMP("mem_merge was failed");
				}
				bmp->release(&bmp);
			}
		}
		free(rgb24_buf);
		rgb24_buf = NULL;
		return 0;
	}
	else
	{
		return -1;
	}
}

static void *thread_send_server(void *arg)
{
	struct camera_buf *buf_tmp = (struct camera_buf *)arg;
	struct camera_class *cam = NULL;

	pthread_mutex_lock(&mutex);

	n_thread++;

	if(NULL != buf_tmp)
	{
		cam = (struct camera_class *)buf_tmp->arg;

		if(V4L2_PIX_FMT_RGB32 == cam->pixel_format)
		{
			if(0 != ready_to_send(cam, (rgb32_class *)buf_tmp->start, buf_tmp->length))
			{
				WAR_DUMP("send file");
			}
		}
		else
		{
			printf("save_bmp: current format is not supported\n");
		}

		cam->free_bufdup(cam, &buf_tmp);
	}

#if THREAD_DEBUG
	printf("T%lx: done\n", pthread_self());
#endif

	n_thread--;

	pthread_mutex_unlock(&mutex);

	return (void *)0;
}

static int device_init(struct camera_class *cam)
{
	if(0 != cam->video_open(cam))
	{
		ERR_DUMP("video_open()");
		return -1;
	}

	cam->show_cap(cam);

	//cam->enum_fmt(cam);

	if(0 != cam->set_fmt(cam))
	{
#if !IGNORE_S_FMT
		ERR_DUMP("set_fmt()");
		return -1;
#else
		WAR_DUMP("set_fmt()");
#endif
	}

#if IGNORE_S_FMT
	errno = 0;
#endif

	if(N_BUF != cam->req_buf(cam, N_BUF))
	{
		ERR_DUMP("req_buf()");
		return -1;
	}

	if(0 != cam->alloc_buf(cam))
	{
		ERR_DUMP("alloc_buf()");
		return -1;
	}

	if(0 != cam->mmap_buf(cam))
	{
		ERR_DUMP("mmap_buf()");
		return -1;
	}

	if(0 != cam->init_buf(cam))
	{
		ERR_DUMP("push_buf()");
		return -1;
	}
	return 0;
}

int main(int argc, char **argv)
{
	int ret = 0;
	
	struct camera_buf *buf_tmp = NULL;

	struct camera_class *cam = NULL;
	
	pthread_t tid = (pthread_t)0;

	if(3 != argc)
	{
		printf("USAGE: %s [DEVICE] [CHANNEL]\n", argv[0]);
		return -1;
	}

	INF_DUMP("Program start...");

	pthread_mutex_init(&mutex, NULL);

	cam = video_init(argv[1], atoi(argv[2]), WIDTH, HEIGHT, V4L2_PIX_FMT_RGB32);

	if(NULL == cam)
	{
		ERR_DUMP("video_init()");
		return -1;
	}

	INF_DUMP("Object create success");

	if(0 != device_init(cam))
	{
		ERR_DUMP("device_init()");
		ret = -1;
		goto free;
	}

	INF_DUMP("Camera init success");

	if(0 != cam->start_stream(cam))
	{
		ERR_DUMP("start_stream()");
		ret = -1;
		goto free;
	}

	INF_DUMP("Camera start streaming");

	cam->ignore_frame(cam, 1, 50);

	INF_DUMP("Camera ignore frame finish");
	
	signal(SIGHUP, signal_handle);
	signal(SIGINT, signal_handle);
	signal(SIGQUIT, signal_handle);
	
	while(0 == loop)
	{
		pthread_mutex_lock(&mutex);

		if(0 != cam->get_frame(cam, 3))
		{
			ERR_DUMP("get_frame()");
			ret = -1;
			goto free;
		}

		INF_DUMP("Camera get frame finish");

		if(0 != cam->pop_buf(cam))
		{
			ERR_DUMP("pop_buf()");
			ret = -1;
			goto free;
		}

		INF_DUMP("Camera pop buffer finish");

		if(NULL == buf_tmp)
		{
			if(0 != cam->get_bufdup(cam, &buf_tmp))
			{
				ERR_DUMP("get_bufdup()");
				ret = -1;
				goto free;
			}
		}
		
		if(NULL == buf_tmp)
		{
			WAR_DUMP("get_bufdup");
		}

		INF_DUMP("Camera dup buffer finish");

		if(0 != cam->push_buf(cam))
		{
			ERR_DUMP("push_buf()");
			ret = -1;
			goto free;
		}

		INF_DUMP("Camera push buffer finish");

		pthread_mutex_unlock(&mutex);
		
		if(NULL != buf_tmp)
		{
			if(0 != pthread_create(&tid, NULL, thread_send_server, buf_tmp))
			{
				WAR_DUMP("Thread creare failed");
				cam->free_bufdup(cam, &buf_tmp);
			}
#if THREAD_DEBUG
			else
			{
				printf("T%lx: start\n", tid);
			}
#endif
			tid = (pthread_t)0;
			buf_tmp = NULL;
		}

		pthread_mutex_lock(&mutex);

		pthread_mutex_unlock(&mutex);
	}
	
	while(0 < n_thread)
	{
		sleep(1);
	}

	INF_DUMP("Everything is OK");

free:
	INF_DUMP("Program release start");
	
	pthread_mutex_destroy(&mutex);

	if(NULL != cam)
	{
		cam->release(&cam);
	}

	INF_DUMP("Program release complete");

	return ret;
}